package cn.edu.cug.cs.gtl.docsrv.shp.querier;

import cn.edu.cug.cs.gtl.common.Pair;
import cn.edu.cug.cs.gtl.docsrv.shp.block.ShapeBlock;
import cn.edu.cug.cs.gtl.docsrv.shp.manager.ShpManager;
import cn.edu.cug.cs.gtl.mybatis.mapper.shp.ShpAll;
import org.jetbrains.annotations.NotNull;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 针对Manager中ShapeBlock的查询器
 */
public class DefaultBlockQuerier implements BlockQuerier {
    List<ShapeBlock>   blocks;
    ShpManager manager;

    /**
     *
     * @param manager
     */
    public DefaultBlockQuerier(ShpManager manager){
        this.blocks= new ArrayList<>();
        this.manager=manager;
    }


    @Override
    public boolean select(Map<String, Object> parameters) {
        try {
            List<ShpAll> shpAllList = this.manager.query(parameters);
            this.blocks.clear();
            for (ShpAll shpAll : shpAllList) {
                this.blocks.add(ShpManager.toBlock(shpAll));
            }
        }
        catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    @Override
    public boolean disjoint(Geometry g) {
        List<Pair<BigDecimal,Envelope>> lp = this.manager.queryAllEnvelopes();
        List<BigDecimal> ids = new ArrayList<>();
        for(Pair<BigDecimal,Envelope> p: lp){
            if(g.getEnvelopeInternal().intersects(p.getSecond())==false){
                ids.add(p.getFirst());
            }
            else{
                continue;
            }
        }
        if(ids.size()==0) return true;
        this.blocks.clear();
        for(BigDecimal i: ids){
            this.blocks.add(new ShapeBlock(this.manager.queryByInternalId(i)));
        }
        return true;
    }

    @Override
    public boolean touches(Geometry g) {
        GeometryFactory gf = new GeometryFactory();
        List<Pair<BigDecimal,Envelope>> lp = this.manager.queryAllEnvelopes();
        List<BigDecimal> ids = new ArrayList<>();
        for(Pair<BigDecimal,Envelope> p: lp){
            if(g.touches(gf.toGeometry(p.getSecond()))){
                ids.add(p.getFirst());
            }
            else{
                continue;
            }
        }
        if(ids.size()==0) return true;
        this.blocks.clear();
        for(BigDecimal i: ids){
            this.blocks.add(new ShapeBlock(this.manager.queryByInternalId(i)));
        }
        return true;
    }

    @Override
    public boolean intersects(Geometry g) {
        GeometryFactory gf = new GeometryFactory();
        List<Pair<BigDecimal,Envelope>> lp = this.manager.queryAllEnvelopes();
        List<BigDecimal> ids = new ArrayList<>();
        for(Pair<BigDecimal,Envelope> p: lp){
            if(g.intersects(gf.toGeometry(p.getSecond()))){
                ids.add(p.getFirst());
            }
            else{
                continue;
            }
        }
        this.blocks.clear();
        if(ids.size()==0)
            return false;
        for(BigDecimal i: ids){
            this.blocks.add(new ShapeBlock(this.manager.queryByInternalId(i)));
        }
        return true;
    }

    @Override
    public boolean crosses(Geometry g) {
        GeometryFactory gf = new GeometryFactory();
        List<Pair<BigDecimal,Envelope>> lp = this.manager.queryAllEnvelopes();
        List<BigDecimal> ids = new ArrayList<>();
        for(Pair<BigDecimal,Envelope> p: lp){
            if(g.crosses(gf.toGeometry(p.getSecond()))){
                ids.add(p.getFirst());
            }
            else{
                continue;
            }
        }
        if(ids.size()==0) return true;
        this.blocks.clear();
        for(BigDecimal i: ids){
            this.blocks.add(new ShapeBlock(this.manager.queryByInternalId(i)));
        }
        return true;
    }

    @Override
    public boolean within(Geometry g) {
        GeometryFactory gf = new GeometryFactory();
        List<Pair<BigDecimal,Envelope>> lp = this.manager.queryAllEnvelopes();
        List<BigDecimal> ids = new ArrayList<>();
        for(Pair<BigDecimal,Envelope> p: lp){
            if(g.within(gf.toGeometry(p.getSecond()))){
                ids.add(p.getFirst());
            }
            else{
                continue;
            }
        }
        if(ids.size()==0) return true;
        this.blocks.clear();
        for(BigDecimal i: ids){
            this.blocks.add(new ShapeBlock(this.manager.queryByInternalId(i)));
        }
        return true;
    }

    @Override
    public boolean contains(Geometry g) {
        GeometryFactory gf = new GeometryFactory();
        List<Pair<BigDecimal,Envelope>> lp = this.manager.queryAllEnvelopes();
        List<BigDecimal> ids = new ArrayList<>();
        for(Pair<BigDecimal,Envelope> p: lp){
            if(g.contains(gf.toGeometry(p.getSecond()))){
                ids.add(p.getFirst());
            }
            else{
                continue;
            }
        }
        if(ids.size()==0) return true;
        this.blocks.clear();
        for(BigDecimal i: ids){
            this.blocks.add(new ShapeBlock(this.manager.queryByInternalId(i)));
        }
        return true;
    }

    @Override
    public boolean overlaps(Geometry g) {
        GeometryFactory gf = new GeometryFactory();
        List<Pair<BigDecimal,Envelope>> lp = this.manager.queryAllEnvelopes();
        List<BigDecimal> ids = new ArrayList<>();
        for(Pair<BigDecimal,Envelope> p: lp){
            if(g.overlaps(gf.toGeometry(p.getSecond()))){
                ids.add(p.getFirst());
            }
            else{
                continue;
            }
        }
        if(ids.size()==0) return true;
        this.blocks.clear();
        for(BigDecimal i: ids){
            this.blocks.add(new ShapeBlock(this.manager.queryByInternalId(i)));
        }
        return true;
    }

    @Override
    public boolean covers(Geometry g) {
        GeometryFactory gf = new GeometryFactory();
        List<Pair<BigDecimal,Envelope>> lp = this.manager.queryAllEnvelopes();
        List<BigDecimal> ids = new ArrayList<>();
        for(Pair<BigDecimal,Envelope> p: lp){
            if(g.covers(gf.toGeometry(p.getSecond()))){
                ids.add(p.getFirst());
            }
            else{
                continue;
            }
        }
        if(ids.size()==0) return true;
        this.blocks.clear();
        for(BigDecimal i: ids){
            this.blocks.add(new ShapeBlock(this.manager.queryByInternalId(i)));
        }
        return true;
    }

    @Override
    public boolean coveredBy(Geometry g) {
        GeometryFactory gf = new GeometryFactory();
        List<Pair<BigDecimal,Envelope>> lp = this.manager.queryAllEnvelopes();
        List<BigDecimal> ids = new ArrayList<>();
        for(Pair<BigDecimal,Envelope> p: lp){
            if(g.coveredBy(gf.toGeometry(p.getSecond()))){
                ids.add(p.getFirst());
            }
            else{
                continue;
            }
        }
        if(ids.size()==0) return true;
        this.blocks.clear();
        for(BigDecimal i: ids){
            this.blocks.add(new ShapeBlock(this.manager.queryByInternalId(i)));
        }
        return true;
    }

    @Override
    public boolean equals(Geometry g) {
        GeometryFactory gf = new GeometryFactory();
        List<Pair<BigDecimal,Envelope>> lp = this.manager.queryAllEnvelopes();
        List<BigDecimal> ids = new ArrayList<>();
        for(Pair<BigDecimal,Envelope> p: lp){
            if(g.equals(gf.toGeometry(p.getSecond()))){
                ids.add(p.getFirst());
            }
            else{
                continue;
            }
        }
        if(ids.size()==0) return true;
        this.blocks.clear();
        for(BigDecimal i: ids){
            this.blocks.add(new ShapeBlock(this.manager.queryByInternalId(i)));
        }
        return true;
    }

    @Override
    public Envelope getAllBlocksEnvelope() {

        Envelope envelope = new Envelope();
        List<Pair<BigDecimal,Envelope>> lp = this.manager.queryAllEnvelopes();
        for(Pair<BigDecimal,Envelope> p: lp){
             envelope.expandToInclude(p.getSecond());
        }
        return envelope;
    }

    @Override
    public Geometry getAllBlocksBoundary() {
        GeometryFactory gf = new GeometryFactory();
        return gf.toGeometry(getAllBlocksEnvelope());
    }

    /**
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    @NotNull
    @Override
    public Iterator<ShapeBlock> iterator() {
        return this.blocks.iterator();
    }
}
